package db

import (
	"cs-backend/conf"
	"cs-backend/model"
	"cs-backend/util"
	"fmt"
	"github.com/xormplus/xorm"
	"github.com/xormplus/xorm/names"
	"gopkg.in/ini.v1"
	"sync"
	"time"

	_ "github.com/go-sql-driver/mysql"
)

var (
	engine       *xorm.Engine
	masterEngine *xorm.Engine
	slaveEngine  *xorm.Engine
	lock         sync.Mutex
)

func GetEngine() *xorm.Engine {
	if nil == engine {
		initEngine()
	}
	return engine
}

func getDbConfig(section string) (*conf.DbConf, error) {
	cfg, err := ini.Load("./static/conf/db.ini")
	if err != nil {
		util.Log.Fatal(err)
		return nil, err
	}
	dbConf := new(conf.DbConf)
	err = cfg.Section(section).MapTo(&dbConf)
	if err != nil {
		util.Log.Fatal(err)
		return nil, err
	}
	//util.Log.Info(json.Marshal(dbConf))
	return dbConf, nil
}

// 初始化 xorm.engine
func init() {
	initEngine()
}

func initEngine() {
	var err error
	lock.Lock()
	defer lock.Unlock()

	dbConf, err := getDbConfig("Master")
	if err != nil {
		util.Log.Fatal(err)
		return
	}

	//连接字符串   root:lzw719@tcp(127.0.0.1:3306)/cs?charset=utf8
	driveSource := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8",
		dbConf.User, dbConf.Password, dbConf.Host, dbConf.Port, dbConf.DbName)
	engine, err = xorm.NewMySQL(dbConf.DriverName, driveSource)
	if err != nil {
		util.Log.Fatal(err)
		return
	}
	// Debug模式，打印全部的SQL语句，帮助对比，看ORM与SQL执行的对照关系
	engine.ShowSQL(true)
	engine.SetTZLocation(conf.SysTimeLocation)
	syncStructure()

	/*--------------------------------------------------------------------------------------------------
	1、使用RegisterSqlMap()注册SqlMap配置
	2、RegisterSqlTemplate()方法注册SSqlTemplate模板配置
	3、SqlMap配置文件总根目录和SqlTemplate模板配置文件总根目录可为同一目录
	--------------------------------------------------------------------------------------------------*/

	//注册SqlMap配置，可选功能，如应用中无需使用SqlMap，可无需初始化
	//此处使用xml格式的配置，配置文件根目录为"./sql/oracle"，配置文件后缀为".xml"
	err = engine.RegisterSqlMap(xorm.Xml("./static/sql/"+dbConf.DriverName, ".xml"))
	if err != nil {
		util.Log.Fatal(err)
	}
	//注册动态SQL模板配置，可选功能，如应用中无需使用SqlTemplate，可无需初始化
	//此处注册动态SQL模板配置，使用Pongo2模板引擎，配置文件根目录为"./sql/oracle"，配置文件后缀为".stpl"
	err = engine.RegisterSqlTemplate(xorm.Pongo2("./static/sql/"+dbConf.DriverName, ".stpl"))
	if err != nil {
		util.Log.Fatal(err)
	}

	//开启SqlMap配置文件和SqlTemplate配置文件更新监控功能，将配置文件更新内容实时更新到内存，如无需要可以不调用该方法
	err = engine.StartFSWatcher()
	if err != nil {
		util.Log.Fatal(err)
	}

	err = engine.Ping()
	if err != nil {
		util.Log.Fatalf("db connect error: %#v\n", err.Error())
	}

	// 30 minute ping db to keep connection
	timer := time.NewTicker(time.Minute * 30)
	go func(x *xorm.Engine) {
		for _ = range timer.C {
			err = x.Ping()
			if err != nil {
				util.Log.Fatalf("db connect error: %#v\n", err.Error())
			}
		}
	}(engine)
}

// 同步实体与数据表
func syncStructure() {
	tbMapper := names.NewPrefixMapper(names.SnakeMapper{}, "t_")
	//colMapper := names.NewSuffixMapper(names.SnakeMapper{}, "_")
	engine.SetTableMapper(tbMapper)
	engine.SetColumnMapper(names.SnakeMapper{})

	err := engine.Sync2(new(model.User),new(model.Role),new(model.Book),new(model.News))

	if err != nil {
		util.Log.Fatalf("sync2 error: ", err)
		return
	}
}
// 关闭数据库
func CloseEngine() {
	err := engine.Close()
	if err != nil {
		util.Log.Fatal(err)
	}
}



// 主库，单例
func InstanceMaster() *xorm.Engine {
	if masterEngine != nil {
		return masterEngine
	}
	lock.Lock()
	defer lock.Unlock()

	dbConf, err := getDbConfig("Master")
	if err != nil {
		util.Log.Fatal(err)
		return nil
	}

	driveSource := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8",
		dbConf.User, dbConf.Password, dbConf.Host, dbConf.Port, dbConf.DbName)
	engine, err := xorm.NewEngine(dbConf.DriverName, driveSource)
	if err != nil {
		util.Log.Fatal("dbhelper.DbInstanceMaster,", err)
		return nil
	}
	// Debug模式，打印全部的SQL语句，帮助对比，看ORM与SQL执行的对照关系
	engine.ShowSQL(false)
	engine.SetTZLocation(conf.SysTimeLocation)

	// 性能优化的时候才考虑，加上本机的SQL缓存
	//cacher := xorm.NewLRUCacher(xorm.NewMemoryStore(), 1000)
	//engine.SetDefaultCacher(cacher)

	masterEngine = engine
	return engine
}

// 从库，单例
func InstanceSlave() *xorm.Engine {
	if slaveEngine != nil {
		return slaveEngine
	}
	lock.Lock()
	defer lock.Unlock()

	dbConf, err := getDbConfig("Master")
	if err != nil {
		util.Log.Fatal(err)
		return nil
	}
	engine, err := xorm.NewEngine(dbConf.DriverName,
		fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8",
			dbConf.User, dbConf.Password, dbConf.Host, dbConf.Port, dbConf.DbName))
	if err != nil {
		util.Log.Fatal("dbhelper", "DbInstanceMaster", err)
		return nil
	}
	engine.SetTZLocation(conf.SysTimeLocation)
	slaveEngine = engine
	return engine
}
